/*
 * SPDX-FileCopyrightText: 2023 IacobIacob01
 * SPDX-License-Identifier: Apache-2.0
 */

package com.dot.gallery.feature_node.presentation.common

import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.layout.LazyLayoutCacheWindow
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.dokar.pinchzoomgrid.PinchZoomGridLayout
import com.dokar.pinchzoomgrid.rememberPinchZoomGridState
import com.dot.gallery.core.Constants.Animation.enterAnimation
import com.dot.gallery.core.Constants.Animation.exitAnimation
import com.dot.gallery.core.Constants.Target.TARGET_TRASH
import com.dot.gallery.core.Constants.cellsList
import com.dot.gallery.core.LocalEventHandler
import com.dot.gallery.core.LocalMediaSelector
import com.dot.gallery.core.Settings.Misc.rememberGridSize
import com.dot.gallery.core.navigate
import com.dot.gallery.core.presentation.components.EmptyMedia
import com.dot.gallery.core.presentation.components.NavigationActions
import com.dot.gallery.core.presentation.components.NavigationButton
import com.dot.gallery.core.presentation.components.SelectionSheet
import com.dot.gallery.core.toggleNavigationBar
import com.dot.gallery.feature_node.domain.model.Media
import com.dot.gallery.feature_node.domain.model.MediaMetadataState
import com.dot.gallery.feature_node.domain.model.MediaState
import com.dot.gallery.feature_node.presentation.common.components.MediaGridView
import com.dot.gallery.feature_node.presentation.common.components.TwoLinedDateToolbarTitle
import com.dot.gallery.feature_node.presentation.search.MainSearchBar
import com.dot.gallery.feature_node.presentation.util.LocalHazeState
import com.dot.gallery.feature_node.presentation.util.Screen
import com.dot.gallery.feature_node.presentation.util.selectedMedia
import dev.chrisbanes.haze.LocalHazeStyle
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class,
    ExperimentalFoundationApi::class
)
@Composable
fun <T: Media> MediaScreen(
    paddingValues: PaddingValues = PaddingValues(0.dp),
    albumId: Long = remember { -1L },
    target: String? = remember { null },
    albumName: String,
    mediaState: State<MediaState<T>>,
    metadataState: State<MediaMetadataState>,
    allowHeaders: Boolean = true,
    showMonthlyHeader: Boolean = false,
    enableStickyHeaders: Boolean = true,
    allowNavBar: Boolean = false,
    customDateHeader: String? = null,
    customViewingNavigation: ((media: T) -> Unit)? = null,
    navActionsContent: @Composable ((expandedDropDown: MutableState<Boolean>, result: ActivityResultLauncher<IntentSenderRequest>) -> Unit),
    emptyContent: @Composable () -> Unit = { EmptyMedia() },
    aboveGridContent: @Composable (() -> Unit)? = remember { null },
    isScrolling: MutableState<Boolean> = remember { mutableStateOf(false) },
    sharedTransitionScope: SharedTransitionScope,
    animatedContentScope: AnimatedContentScope,
    onActivityResult: (result: ActivityResult) -> Unit,
) {
    val showSearchBar = remember(albumId, target) { albumId == -1L && target == null }
    var canScroll by rememberSaveable { mutableStateOf(true) }
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
        state = rememberTopAppBarState(),
        canScroll = { canScroll },
        flingAnimationSpec = null
    )
    var lastCellIndex by rememberGridSize()

    val dpCacheWindow = LazyLayoutCacheWindow(ahead = 200.dp, behind = 100.dp)
    val pinchState = rememberPinchZoomGridState(
        cellsList = cellsList,
        initialCellsIndex = lastCellIndex,
        gridState = rememberLazyGridState(
            cacheWindow = dpCacheWindow
        )
    )

    LaunchedEffect(pinchState.isZooming) {
        withContext(Dispatchers.IO) {
            canScroll = !pinchState.isZooming
            lastCellIndex = cellsList.indexOf(pinchState.currentCells)
        }
    }
    val eventHandler = LocalEventHandler.current
    val selector = LocalMediaSelector.current
    val selectionState = selector.isSelectionActive.collectAsStateWithLifecycle()
    val selectedMedia = selector.selectedMedia.collectAsStateWithLifecycle()

    LaunchedEffect(selectionState.value) {
        if (allowNavBar) {
            eventHandler.toggleNavigationBar(!selectionState.value)
        }
    }

    Box(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(LocalLayoutDirection.current),
                end = paddingValues.calculateEndPadding(LocalLayoutDirection.current)
            )
    ) {
        val scaffoldModifier = remember(showSearchBar) {
            if (!showSearchBar) Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
            else Modifier
        }
        Scaffold(
            modifier = scaffoldModifier,
            topBar = {
                AnimatedVisibility(
                    visible = !showSearchBar,
                    enter = enterAnimation,
                    exit = exitAnimation
                ) {
                    LargeTopAppBar(
                        modifier = Modifier.hazeEffect(
                            state = LocalHazeState.current,
                            style = LocalHazeStyle.current
                        ),
                        title = {
                            TwoLinedDateToolbarTitle(
                                albumName = albumName,
                                dateHeader = customDateHeader ?: mediaState.value.dateHeader
                            )
                        },
                        navigationIcon = {
                            NavigationButton(
                                albumId = albumId,
                                target = target,
                                alwaysGoBack = true,
                            )
                        },
                        actions = {
                            NavigationActions(
                                actions = navActionsContent,
                                onActivityResult = onActivityResult
                            )
                        },
                        scrollBehavior = scrollBehavior,
                        colors = TopAppBarDefaults.topAppBarColors(
                            scrolledContainerColor = MaterialTheme.colorScheme.surface,
                        ),
                    )
                }
                AnimatedVisibility(
                    visible = showSearchBar,
                    enter = enterAnimation,
                    exit = exitAnimation
                ) {
                    MainSearchBar(
                        isScrolling = isScrolling,
                        sharedTransitionScope = sharedTransitionScope,
                        animatedContentScope = animatedContentScope,
                    ) {
                        NavigationActions(
                            actions = navActionsContent,
                            onActivityResult = onActivityResult
                        )
                    }
                }
            }
        ) { it ->
            PinchZoomGridLayout(
                state = pinchState,
                modifier = Modifier.hazeSource(LocalHazeState.current)
            ) {
                MediaGridView(
                    modifier = Modifier.padding(top = it.calculateTopPadding()),
                    mediaState = mediaState,
                    metadataState = metadataState,
                    paddingValues = remember(paddingValues, it) {
                        PaddingValues(
                            bottom = paddingValues.calculateBottomPadding() + 128.dp
                        )
                    },
                    searchBarPaddingTop = remember(paddingValues) {
                        paddingValues.calculateTopPadding()
                    },
                    showSearchBar = showSearchBar,
                    allowSelection = true,
                    canScroll = canScroll,
                    allowHeaders = allowHeaders,
                    enableStickyHeaders = enableStickyHeaders,
                    showMonthlyHeader = showMonthlyHeader,
                    aboveGridContent = aboveGridContent,
                    isScrolling = isScrolling,
                    emptyContent = emptyContent,
                    sharedTransitionScope = sharedTransitionScope,
                    animatedContentScope = animatedContentScope,
                    onMediaClick = {
                        if (customViewingNavigation == null) {
                            val albumRoute = "albumId=$albumId"
                            val targetRoute = "target=$target"
                            val param =
                                if (target != null) targetRoute else albumRoute
                            eventHandler.navigate(Screen.MediaViewScreen.route + "?mediaId=${it.id}&$param")
                        } else {
                            customViewingNavigation(it)
                        }
                    },
                )
            }
        }
        AnimatedVisibility(
            modifier = Modifier
                .align(Alignment.BottomEnd),
            visible = remember(target) { target != TARGET_TRASH },
            enter = enterAnimation,
            exit = exitAnimation
        ) {
            val selectedMediaList = mediaState.value.media.selectedMedia(selectedSet = selectedMedia)
            SelectionSheet(
                modifier = Modifier
                    .align(Alignment.BottomEnd),
                allMedia = mediaState.value,
                selectedMedia = selectedMediaList
            )
        }
    }
}